package com.sikong.geotools.featureTutorial;

import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.swing.data.JFileDataStoreChooser;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;


import javax.swing.*;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 这段代码主要有以下几个方面：
 * 参考 csv2shp.md
 * 1、通过窗口化界面选择csv文件读取路径
 * 2、通过窗口化界面选择shp文件保存路径
 * 3、整体上将csv文件转为shp文件
 *
 * @see <a href="https://docs.geotools.org/latest/userguide/tutorial/feature/csv2shp.html">Feature Tutorial</a>
 *
 * @author 梁超 / stan
 * @since  0.1.0
 * @Date   2022/03/11
 */
public class Csv2shp {

    private String typeName;
    private String typeSpec;
    private File file;
    private SimpleFeatureType type;
    private ShapefileDataStore newDataStore;

    /**
     * 通过设定Getter和Setter方法，在满足外界能访问的同时，也保证了私有化属性的安全与稳定
     * 涉及到的名词解释：
     *                  英文             中文           举例（通俗理解）
     *               featureType        要素类          GIS中的图层文件
     *               typeSpec          要素类设定       GIS中要素属性表的表结构，即每列的属性
     *               typeName          要素类名称       GIS中图层文件的名称
     *               feature           要素            GIS中图层文件中的要素
     *
     */

    public String getTypeName() {
        return typeName;
    }

    public void setTypeName(String typeName) {
        this.typeName = typeName;
    }

    public String getTypeSpec() {
        return typeSpec;
    }

    public void setTypeSpec(String typeSpec) {
        this.typeSpec = typeSpec;
    }

    /**
     * 通过JFileDataStoreChooser构建窗口化界面选择csv文件，初始化路径为resources目录相对路径
     * 通过if判断是否选中文件，保证程序正常退出
     */
    public void selectInputCSV() throws Exception{
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        this.file = JFileDataStoreChooser.showOpenFile("csv", new File("src/main/resources"), null);
        if (file == null) {
            System.exit(0);
        }
    }

    /**
     * 通过DataUtilities工具类，传入typeName和typeSpec参数，创建featureType要素类
     */
    private void mkType() throws SchemaException {

        this.type = DataUtilities.createType(this.typeName, this.typeSpec);
    }

    /**
     *  该方法用于读取csv文件，并转为feature
     *  1、构建featureBuilder，按typeSpec顺序add属性
     *  2、构建geometryFactory，将lon,lat转为point类型
     *  2、遍历读取csv文件每一行，通过readLine方法先调用一次自动next下一行的操作，忽略第一行（表头）
     *
     * @return 添加完成所有属性的feature
     */
    private List<SimpleFeature> mkFeat() throws Exception {

        List<SimpleFeature> features = new ArrayList<>();

        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(this.type);

        try (BufferedReader reader = new BufferedReader(new FileReader(this.file))) {
            String line = reader.readLine();
            System.out.println("Header: " + line);

            for (line = reader.readLine(); line != null; line = reader.readLine()) {
                if (line.trim().length() > 0) {
                    String[] tokens = line.split("\\,");

                    double latitude = Double.parseDouble(tokens[0]);
                    double longtitude = Double.parseDouble(tokens[1]);
                    String name = tokens[2].trim();
                    int number = Integer.parseInt(tokens[3].trim());

                    Point point = geometryFactory.createPoint(new Coordinate(longtitude, latitude));

                    featureBuilder.add(point);
                    featureBuilder.add(name);
                    featureBuilder.add(number);
                    SimpleFeature feature = featureBuilder.buildFeature(null);

                    features.add(feature);
                }
            }
        }
        return features;
    }

    /**
     * 通过JFileDataStoreChooser构建窗口化界面选择shp输出路径，初试路径为csv读取路径
     * 通过判断语句保证程序正常退出
     *
     * @return 返回shp输出路径的File对象
     */
    private File selectOutputSHP() throws Exception {
        String path = this.file.getAbsolutePath();
        String newPath = path.substring(0,path.length() - 4) + ".shp";

        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        JFileDataStoreChooser chooser = new JFileDataStoreChooser("shp");
        chooser.setDialogTitle("Save shapefile");
        chooser.setSelectedFile(new File(newPath));

        int returnVal = chooser.showSaveDialog(null);

        if (returnVal != JFileDataStoreChooser.APPROVE_OPTION) {
            System.exit(0);
        }

        File newFile = chooser.getSelectedFile();
        if (newFile.equals(this.file)) {
            System.out.println("Error: cannot replace " + this.file);
            System.exit(0);
        }

        return newFile;

    }

    /**
     * 通过新建dataStore，再调用creatSchema方法，将构建好的要素类传入，得到一个具有属性的dataStore
     */
    private void mkSHP() throws Exception {
        File file = this.selectOutputSHP();

        ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

        HashMap<String, Serializable> params = new HashMap<>();
        params.put("url",file.toURI().toURL());
        params.put("creat spatial index", Boolean.TRUE);

//        this.newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
        this.mkType();
        this.newDataStore.createSchema(this.type);

    }

    /**
     * 1、利用构建好的dataStore得到一个featureSource
     * 2、利用featureSource得到一个featureStore
     * 3、将featureType、feature用SimpleFeatureCollection进行封装
     * 4、将封装好的collection传入featureStore
     * 5、通过transaction将featureStore传入shp文件中
     */
    public void wri2SHP() throws Exception {
        Transaction transaction = new DefaultTransaction("creat");
        this.mkSHP();
        String typeName = this.newDataStore.getTypeNames()[0];
        ContentFeatureSource featureSource = this.newDataStore.getFeatureSource(typeName);
        SimpleFeatureType SHAPE_TYPE = featureSource.getSchema();

        System.out.println("SHAPE: " + SHAPE_TYPE);

        if (featureSource instanceof SimpleFeatureSource) {
            SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

            SimpleFeatureCollection collection = new ListFeatureCollection(this.type, this.mkFeat());
            featureStore.setTransaction(transaction);
            try {
                featureStore.addFeatures(collection);
                transaction.commit();
            } catch (Exception problem) {
                problem.printStackTrace();
                transaction.rollback();
            } finally {
                transaction.close();
            }
            System.exit(0);
        } else {
            System.out.println(typeName + "does not support read/write access");
            System.exit(1);
        }
    }
}
